---
title: "Retries"
group: "Getting Started"
groupOrder: 6
---

Sometimes its useful to have some retry functionality in your actions in case something goes wrong and an error is throwing in your action.

## Retries on Actions

To configure retying on your server action, you can add the `retry` method:

```ts title="actions.ts"
"use server"

import { createServerAction } from "zsa"
import z from "zod"

export const retryExample = createServerAction()
    .retry({ // [!code highlight]
        maxAttempts: 3 // [!code highlight]
    }) // [!code highlight]
    .handler(async () => {
        // throw an error 50% of the time
        if (Math.random() > 0.5) {
            throw new Error("Random error")
        }
        return "Success!"
    })
```

In this example, this server action will attempt to execute up to 3 times. It will only retry the action if an uncaught error is thrown. If the error still persists after the 3 attempts, then the server action will throw the error to the top level.

Additionally, you can add a delay between retries. This can be useful for things such as rate limit errors, where you may want to wait a certain amount of time before executing the server action again.

```ts title="actions.ts"
"use server"

import { createServerAction } from "zsa"
import z from "zod"

export const retryWithDelay = createServerAction()
    .retry({
        maxAttempts: 3,
        delay: 1000 // [!code highlight]
    })
    .handler(async () => {
        //throw an error 50% of the time
        if (Math.random() > 0.5) {
            throw new Error("Random error")
        }
        return "Success!"
    })
```

In this example, the server action will wait 1 second between each retry.

Sometimes, you dont want to wait a fixed amount of time depending on certain conitions. In this case, you can set the `delay` to be a function that takes in a `currentAttempt` and the `err` that was throw.

```ts title="actions.ts"
"use server"

import { createServerAction } from "zsa"
import z from "zod"

export const retryWithProgressiveDelay = createServerAction()
    .retry({
        maxAttempts: 3,
        delay: (currentAttempt, err) => { // [!code highlight]
            return 1000 * currentAttempt // [!code highlight]
        }, // [!code highlight]
    })
    .handler(async () => {
        //throw an error 50% of the time
        if (Math.random() > 0.5) {
            throw new Error("Random error")
        }
        return "Success!"
    })
```

In this example, the delay waits 1 second on the first attempt, 2 seconds on the second attempt, and 3 seconds on the third attempt.

## Retries on Procedures

You can also set retires on procedures if you want a set of actions to inherit this retry functionality.

```ts title="actions.ts"
"use server"

import { createServerAction } from "zsa"
import z from "zod"

export const retryProcedure = createServerActionProcedure()
    .retry({
        maxAttempts: 3
    })
    .handler(async () => {
        if (Math.random() > 0.5) {
            throw new Error("Random error")
        }
        return {
            user: {
                id: "123",
                email: "test@example.com"
            }
        }
    })

export const exampleAction = retryProcedure
    .createServerAction()
    .handler(async ({ctx}) => {
        return ctx
    })
```

By adding `retry` onto a procedure, `exampleAction` and all other actions made from this procedure will be retried according to `maxAttempts` before an error throw to the top level.

<Note>
  If a `retry` is set on a procedure AND a separate `retry` is set on an action
  that inherits that procedure, then the `retry` on the action will override the
  `retry` on the procedure.
</Note>

<Note>
  If a `timeout` is set on an action or a procedure that utilizes a `retry`,
  then the `timeout` takes precidence over the timeout. This means that if a
  `timeout` runs out of time, then the action will error out with a `ZSAError`
  (with a code of `"TIMEOUT"`)
</Note>
